package com.zzl.bonds.ticket.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzl.apidoc.utils.UuidUtils;
import com.zzl.bonds.ticket.constant.*;
import com.zzl.bonds.ticket.constant.status.*;
import com.zzl.bonds.ticket.constant.type.*;
import com.zzl.bonds.ticket.dao.TicketOpenDao;
import com.zzl.bonds.ticket.dto.*;
import com.zzl.bonds.ticket.dto.KeyValue;
import com.zzl.bonds.ticket.dto.common.MsgInfoDto;
import com.zzl.bonds.ticket.dto.credit.CreditQuota;
import com.zzl.bonds.ticket.dto.feign.*;
import com.zzl.bonds.ticket.dto.ticketopen.*;
import com.zzl.bonds.ticket.entity.*;
import com.zzl.bonds.ticket.entity.feign.ContractEntity;
import com.zzl.bonds.ticket.exception.SysExceptionFactory;
import com.zzl.bonds.ticket.feign.*;
import com.zzl.bonds.ticket.manager.SendMessageManager;
import com.zzl.bonds.ticket.manager.TicketFlowChartManager;
import com.zzl.bonds.ticket.service.*;
import com.zzl.bonds.ticket.service.common.*;
import com.zzl.bonds.ticket.untils.NumberToCN;
import com.zzl.bonds.ticket.untils.StrUtils;
import com.zzl.bonds.ticket.untils.TicketNoUtils;
import com.zzl.general.common.constant.Constant;
import com.zzl.general.common.exception.SysException;
import com.zzl.general.common.feign.dto.Enterprise;
import com.zzl.general.common.requestparameter.StringIds;
import com.zzl.general.common.serurity.entity.User;
import com.zzl.general.common.utils.*;
import com.zzl.sharding.user.entity.UserEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.zzl.bonds.ticket.exception.SysExceptionEnum.*;

/**
 * 开立申请表
 *
 * @author sunpp
 * @date 2022-02-16 14:04:05
 */
@Slf4j
@Service("TicketOpenService")
public class TicketOpenServiceImpl extends ServiceImpl<TicketOpenDao, TicketOpenEntity> implements TicketOpenService {

    @Autowired
    private SupplierCreditService supplierCreditService;

    @Autowired
    private CoreEnterpriseCreditService coreEnterpriseCreditService;

    @Autowired
    private TicketDataService ticketDataService;

    @Autowired
    private BaseDataFeign baseDataFeign;

    @Autowired
    private TicketOpenHandStatusService ticketOpenHandStatusService;

    @Autowired
    private TicketSignHandStatusService ticketSignHandStatusService;

    @Autowired
    private TicketService ticketService;

    @Autowired
    private TicketCommonService ticketCommonService;

    @Autowired
    private SendMsgService sendMsgService;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private UserFeign userFeign;

    @Autowired
    private TicketFinanceService ticketFinanceService;
    @Autowired
    private ContractFeign contractFeign;
    @Autowired
    private TicketProcessService ticketProcessService;

    @Autowired
    private TicketTransferService ticketTransferService;

    @Autowired
    private BusinessContractService businessContractService;

    @Autowired
    private ProcessFeign processFeign;

    @Autowired
    private TicketOperateLogService ticketOperateLogService;

    @Autowired
    private MailMessageService mailMessageService;

    @Autowired
    private TicketFlowChartService ticketFlowChartService;
    @Autowired
    private TicketCirculationEnterpriseService ticketCirculationEnterpriseService;

    /**
     * 分页查询
     *
     * @param params 查询参数
     * @return 分页数据
     */
    @Override
    public PageUtils queryPage(Map<String, Object> params, User user) throws SysException {
        //排序字段：create_time
        //排序方式 order:  desc asc
        /*TODO*/
        System.out.println("!!!!!!!########$$$$$%%");
        //{ticketOpenStartTime=2024-05-08, ticketOpenEndTime=2024-11-04, handStatus=1, sort=create_time, order=desc, currPage=1, pageSize=10}
        System.out.println(params);
        System.out.println("%%%%%%%%%%%%%%%%%%%%");

        //整理page对象，
        // 设置排序字段（PAGE）
        String pageField = (String) params.get(Constant.PAGE);
        // 排序方式（ORDER）
        String order = (String) params.get(Constant.ORDER);
        //Query1方法参数   params，  Query2方法参数  params,排序字段，是否正序，此处用到方法二
        //StringUtils.isBlank(pageField) 判断排序字段是否为空，空就按  create_time ，否则按  pageField，最后true是正序，false是倒序
        IPage<TicketOpenPage> page = new Query<TicketOpenPage>()
                .getPage(params, StringUtils.isBlank(pageField)
                        ? StringConstant.CREATE_TIME_FILED : pageField, Constant.ASC.equals(order));
        //整理params查询参数，
        // 设置处理状态对应的业务状态（HAND_STATUS）
        String handStatusStr = (String) params.get("handStatus");
        if (StringUtils.isNotBlank(handStatusStr)) {
            Integer handStatus = Integer.parseInt(handStatusStr);
            List<Integer> HandStatusList = null;
            if (HandStatusEnum.OPEN_ING.getCode().equals(handStatus)) {
                HandStatusList = TicketStatusEnum.getHandStatusList(handStatus);
            } else if (HandStatusEnum.SUCCESS.getCode().equals(handStatus)) {
                HandStatusList = TicketStatusEnum.getHandStatusList(handStatus);
            } else if (HandStatusEnum.FAIL.getCode().equals(handStatus)) {
                HandStatusList = TicketStatusEnum.getHandStatusList(handStatus);
            } else if(HandStatusEnum.ALL.getCode().equals(handStatus)) {
                HandStatusList = TicketStatusEnum.getHandStatusList(handStatus);
            }
            System.out.println("%%%%%%%%%%%%%%%%%%%%");
            System.out.println(HandStatusList);
            System.out.println("%%%%%%%%%%%%%%%%%%%%");
            params.put("handStatus", HandStatusList);
        }
        // 要将登录用户的企业id添加到查询条件（ENTERPRISE_ID）中，
        params.put(StringConstant.ENTERPRISE_ID,user.getEnterpriseId());
        // 将开立金额（startAmount和endAmount）万为单位的数字转化成个为单位的数组（ticketCommonService.changeTenThousand2Number）
        ticketCommonService.changeTenThousand2Number(params, "startAmount");
        ticketCommonService.changeTenThousand2Number(params, "endAmount");
        //调用数据库对象查询数据（baseMapper.queryPage）
        IPage<TicketOpenPage> ticketOpenPageIPage = baseMapper.queryPage(page , params);
        //设置开立列表票据处理情况及气泡展示内容，处理进度，涉及到全局状态业务逻辑，调用现有方法（ticketOpenHandStatusService.setOpenTicketHandleSituation）
        ticketOpenHandStatusService.setOpenTicketHandleSituation(ticketOpenPageIPage.getRecords());
        //设置开立按钮操作，涉及到全局状态业务逻辑，调用现有方法（ticketOpenHandStatusService.setOpenTicketOperates）
        ticketOpenHandStatusService.setOpenTicketOperates(ticketOpenPageIPage.getRecords());
        PageUtils pageUtils = new PageUtils(page);
        return pageUtils;
    }


    /**
     * 开立签收笔数（今日）：当天已经签收了的笔数是多少 签收完成的
     * 开立签收金额（今日）：当天已经签收了的金额时多少 签收完成的金额
     * 开立签收中金额：所有处理状态为签收中的票据的金额 所有状态为签收中金额
     * <p>
     * 2022/1/24 开立签收笔数，开立签收今日
     *
     * @param user 当前登录用户
     * @return 开立签收的 统计数据
     */
    @Override
    public StatisticsDataDto getOpenSignData(User user) {
        return this.baseMapper.getOpenSignDataDto(user.getEnterpriseId());
    }

    /**
     * 开立笔数：所有，成功的笔数
     * 开单金额（今日）：所有今日成功的金额
     * 开立中金额为处理状态为开立中的所有数据的金额的和
     *
     * @param user 当前登录用户
     * @return 开立的 统计数据
     */
    @Override
    public StatisticsDataDto getOpenData(User user) {
        /*TODO*/
        if (user.getUserCategory().equals(UserCategoryConstant.USER_CATEGORY_GROUP.getKey())) {
            return this.baseMapper.getOpenDataDto(user.getEnterpriseId());
        }
        return null;
    }

    /**
     * 查询票详情
     *
     * @param id   开票列表id
     * @param user 当前登录用户
     * @return 票据详情
     * @throws SysException 业务异常
     */
    @Override
    public OpenDetailDto getOpenDetail(String id, User user) throws SysException {
        /*TODO*/
        TicketOpenEntity entity = this.getById(id);
        OpenDetailDto result = BeanUtil.copyProperties(entity, OpenDetailDto.class);
        // 授信使用情况
        CreditQuota creditQuota = coreEnterpriseCreditService.getCreditOrQuotaById(entity.getCoreEnterpriseCreditId());
        if (!ObjectUtils.isEmpty(creditQuota)) {
            result.setCreditUseInfo(creditQuota.getInstitutyName() + "-" +
                    CreditTypeEnum.getValue(creditQuota.getCreditType()) + "（" + creditQuota.getCreditNo() + "）");
        }
        String surplusDays = ticketCommonService.calculateSurplusDays(entity.getExpireTime(), entity.getTicketStatus());
        result.setSurplusDays(surplusDays);

        // 设置处理状态和进度
        ticketOpenHandStatusService.setOpenTicketHandleSituation(Collections.singletonList(result));
        // 找到开立相关附件信息
        List<TicketDataEntity> dataEntities = ticketDataService.getByTicketId(id, TicketActionTypeEnum.TICKET_TYPE_OPEN.getCode());
        if (!CollectionUtils.isEmpty(dataEntities)) {
            Map<String, List<TicketDataEntity>> map = dataEntities.stream().collect(Collectors.groupingBy(TicketDataEntity::getDataType));
            result.setInvoiceList(map.get(DataTypeEnum.INVOICE.getCode()));
            result.setProofList(map.get(DataTypeEnum.TICKET.getCode()));
        }
        //签署资料
        List<BusinessContractEntity> businessContractEntities = businessContractService.getByTicketId(id, BusinessTypeEnum.TICKET_OPEN.getCode());
        if (!CollectionUtils.isEmpty(businessContractEntities)) {
            ticketFinanceService.getContractSigner(businessContractEntities);
            result.setProtocolList(businessContractEntities);
        }
        //流转企业
        QueryWrapper<TicketCirculationEnterpriseEntity> entityQueryWrapper = new QueryWrapper<>();
        entityQueryWrapper.lambda().eq(TicketCirculationEnterpriseEntity::getTicketOpenId, id);
        List<TicketCirculationEnterpriseEntity> circulationEnterpriseEntityList = ticketCirculationEnterpriseService.list(entityQueryWrapper);
        result.setCirculationEnterprise(circulationEnterpriseEntityList);
        return result;
    }


    /**
     * 设置签署资料
     *
     * @param openDetailDto 开立详情
     * @return
     */
    private List<ContractEntity> setOpenContract(OpenTicketCheckDetailDto openDetailDto) {
        TicketContractListDto ticketContractListDto = new TicketContractListDto();
        ticketContractListDto.setTicketId(openDetailDto.getId());
        ticketContractListDto.setTicketType(ContractTicketTypeEnum.TICKET_TYPE_OPEN_SIGN.getCode());
        List<ContractEntity> contractEntities = contractFeign.ticketContractList(ticketContractListDto);
        return contractEntities;
    }

    @Override
    public OpenDetailDto getOpenTicketCheckDetailDto(String id, User user) throws SysException {
        /*TODO*/
        return new OpenDetailDto();
    }

    @Override
    public OpenDetailDto getTicketOpenSignDetail(String id, User user) throws SysException {
        TicketOpenEntity entity = this.getById(id);
        if (ObjectUtils.isEmpty(entity)) {
            throw new SysException("未找到当前票据信息");
        }
        OpenDetailDto dto = new OpenDetailDto();
        BeanUtils.copyProperties(entity, dto);
        // 授信使用情况
        CreditQuota creditQuota = coreEnterpriseCreditService.getCreditOrQuotaById(entity.getCoreEnterpriseCreditId());
        if (!ObjectUtils.isEmpty(creditQuota)) {
            dto.setCreditUseInfo(creditQuota.getInstitutyName() + "-" +
                    CreditTypeEnum.getValue(creditQuota.getCreditType()) + "（" + creditQuota.getCreditNo() + "）");
        }
        String surplusDays = ticketCommonService.calculateSurplusDays(entity.getExpireTime(), entity.getTicketStatus());
        dto.setSurplusDays(surplusDays);

        // 设置处理状态和进度
        ticketOpenHandStatusService.setOpenTicketHandleSituation(Collections.singletonList(dto));
        // 找到开立相关附件信息
        List<TicketDataEntity> dataEntities = ticketDataService.getByTicketId(id, TicketActionTypeEnum.TICKET_TYPE_OPEN.getCode());
        if (!CollectionUtils.isEmpty(dataEntities)) {
            Map<String, List<TicketDataEntity>> map = dataEntities.stream().collect(Collectors.groupingBy(TicketDataEntity::getDataType));
            dto.setInvoiceList(map.get(DataTypeEnum.INVOICE.getCode()));
            dto.setProofList(map.get(DataTypeEnum.TICKET.getCode()));
        }
        //签署资料
        List<BusinessContractEntity> businessContractEntities = businessContractService.getByTicketId(id, BusinessTypeEnum.TICKET_OPEN_SIGN.getCode());
        if (!CollectionUtils.isEmpty(businessContractEntities)) {
            dto.setProtocolList(businessContractEntities);
        }
        this.getApUserShowInfoList(entity, dto, ApproveTypeEnum.SIGN_OPEN_TRANSFER.getCode());
        return dto;
    }

    private void getApUserShowInfoList(TicketOpenEntity ticketOpenEntity, OpenDetailDto dto, String approveType) throws SysException {
        QueryWrapper<TicketProcessEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(TicketProcessEntity::getApproveType, approveType)
                .eq(TicketProcessEntity::getTicketOpenId, ticketOpenEntity.getId());
        TicketProcessEntity one = ticketProcessService.getOne(queryWrapper);
        if (one != null) {
            dto.setApUserShowInfos(ticketProcessService.getApUserShowInfoList(one));
        }
    }

    @Override
    public OpenPremiseDto getOpenPremise(User user) throws SysException {
        if (ObjectUtils.isEmpty(user.getEnterpriseId())) {
            throw new SysException("当前登录用户的所属企业信息为空");
        }
        if (!user.getUserCategory().equals(UserCategoryConstant.USER_CATEGORY_ENTERPRISE.getKey())) {
            throw new SysException("当前登录用户不是核心企业");
        }
        return null;
    }

    /**
     * 新增
     *
     * @param ticketOpenAddDto 新增实体
     * @param user             当前登录用户
     * @throws SysException 业务异常
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String saveTicketOpen(TicketOpenAddDto ticketOpenAddDto, User user) throws SysException {
        log.info("凭证开具,入参：{}{}{}", ticketOpenAddDto.toString(), "当前用户：", user.toString());
        TicketOpenEntity entity = new TicketOpenEntity();
        //1 票据信息校验
        verityTicketOpen(ticketOpenAddDto);
        //2 核心企业授信额度的验证 并获取 金融机构信息
        verityOpenCredit(ticketOpenAddDto, entity);
        //3 额度开单状态
        updateTicketOpenStatus(entity, user);
        //4 设置企业信息并保存
        setEnterpriseName(entity);
        if (!this.save(entity)) {
            throw new SysException("A00001", "保存失败！");
        }
        //5 核心企业授信相关处理 新增冻结金额，当开立审核完成时 签收完成时 扣减总的授信额度
        coreEnterpriseCreditService.handCoreCredit(ticketOpenAddDto.getCoreEnterpriseCreditId(),
                ticketOpenAddDto.getTicketAmount(), user);
        //6 保存开立相关的附件资料
        ticketOpenAddDto.getProofList().addAll(ticketOpenAddDto.getInvoiceList());
        saveTicketOpenData(ticketOpenAddDto.getProofList(), user, entity);
        //7 凭证操作日志
        ticketOperateLogService.saveTicketLog(LogActionTypeEnum.TICKET_TYPE_OPEN.getCode(), entity.getId()
                , TicketLogStatusEnum.START.getCode(), OperateLogTypeEnum.OPEN.getCode(), TicketLogStatusEnum.OPEN_PENDING_REVIEW.getCode(), user
                , entity.getRemark(),
                entity.getReceiveEnterpriseId(), entity.getReceiveEnterpriseName(),
                entity.getOpenEnterpriseId(), entity.getOpenEnterpriseName(), "", "0", null, null);
        //8 拉起开单复核合同协议
        ticketFinanceService.setBusinessProtocol(entity.getId(), ContractTicketTypeEnum.TICKET_TYPE_OPEN.getCode());
        //9 拉起审批流程
        //拉起开立复核流程，并给复核人发送消息
        MsgInfoDto msgInfoDto = mailMessageService.setTicketOpenMsg(entity, user);
        ticketProcessService.saveTicketProcess(entity.getTicketAmount()
                , entity.getId(), JSON.toJSONString(msgInfoDto), ApproveTypeEnum.OPEN_EXAMING, user
                , entity.getReceiveEnterpriseId()
                , TicketActionTypeEnum.TICKET_TYPE_OPEN.getCode().toString());


        //10 处理流转企业
        this.setCirculationEnterprise(ticketOpenAddDto.getCirculationEnterpriseList(), entity);
        //11 流程轨迹
        this.saveOpenFlowChart(entity, user);
        log.info("凭证开具成功", entity.toString());
        return entity.getId();
    }

    /**
     * 票据信息校验
     *
     * @param ticketOpenAddDto 新增票据dto
     * @throws SysException 业务异常
     */
    private void verityTicketOpen(TicketOpenAddDto ticketOpenAddDto) throws SysException {
        //1 核心企业授信额度验证开立金额（ticketAmount）大于等于0

        //2 兑付日期（expireTime）不能选择周六周日

        //3 兑付日期（expireTime）与开立日期（invoiceTime）不可超过一年 invoiceTime  cashTime

        //4 开立日期（invoiceTime）不能小于当前时间

        //5 开立日期（invoiceTime）必须小于兑付日期（expireTime）
        /*TODO*/
    }

    /**
     * 额度开单状态
     *
     * @param entity 开立申请表对象
     * @param user   当前登录用户
     */
    private void updateTicketOpenStatus(TicketOpenEntity entity, User user) throws SysException {
        /*TODO*/
    }

    /**
     * 设置企业名称
     *
     * @param entity 实体
     * @throws SysException 业务异常
     */
    private void setEnterpriseName(TicketOpenEntity entity) throws SysException {
        /*通过entity对象获取开票方id，签收方id，创建方id和企业id，通过feign请求B服务（baseDataFeign.getEnterPriseByIds），获取对应企业信息*/

        /*遍历企业信息，设置各方（openEnterpriseName，receiveEnterpriseName，enterpriseName）名称，注意开票方和签收方式互斥的*/
        /*TODO*/
    }


    /**
     * 核心企业授信额度的验证 并获取 金融机构信息
     *
     * @param ticketOpenAddDto 新增实体
     * @param entity           开立申请表对象
     * @throws SysException 可能因为额度而抛出异常
     */
    private void verityOpenCredit(TicketOpenAddDto ticketOpenAddDto, TicketOpenEntity entity) throws SysException {
        coreEnterpriseCreditService.getOpenCredit(ticketOpenAddDto, entity);
        BeanUtils.copyProperties(ticketOpenAddDto, entity);
        entity.setId(UuidUtils.getUuid());
        entity.setTicketNo(TicketNoUtils.getTicketNo(redisTemplate));
    }


    /**
     * 保存凭证开立的流程轨迹
     *
     * @param ticketOpenEntity 开立申请
     * @param user             用户
     * @throws SysException 异常
     */
    private void saveOpenFlowChart(TicketOpenEntity ticketOpenEntity, User user) throws SysException {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(com.zzl.bonds.ticket.untils.DateUtils.DF_YYYY_MM_DD);
        TicketFlowChartEntity ticketFlowChartEntity = new TicketFlowChartEntity();
        ticketFlowChartEntity.setTitle("凭证编号：" + ticketOpenEntity.getTicketNo());
        ticketFlowChartEntity.setInitiatorEnterpriseName("开立企业：" + ticketOpenEntity.getOpenEnterpriseName());
        ticketFlowChartEntity.setFirstTime("开立日期：" + simpleDateFormat.format(ticketOpenEntity.getCreateTime()));
        ticketFlowChartEntity.setTwoTime("到期日期：" + simpleDateFormat.format(ticketOpenEntity.getExpireTime()));
        ticketFlowChartEntity.setMoneyAmountFirst("凭证金额：" + StrUtils.simgleFormat(ticketOpenEntity.getTicketAmount()) + " 元");
        ticketFlowChartEntity.setUppercaseAmount("人民币" + NumberToCN.number2CNMontrayUnit(BigDecimal.valueOf(ticketOpenEntity.getTicketAmount())));
        ticketFlowChartEntity.setTicketStatus(FlowChartColourEnum.YELLOW.getCode());
        ticketFlowChartEntity.setApplyId(ticketOpenEntity.getId());
        ticketFlowChartEntity.setType(FlowChartTypeEnum.OPEN.getCode());
        ticketFlowChartEntity.setTicketOpenId(ticketOpenEntity.getId());
        ticketFlowChartService.saveTicketFlowChartEntity(Collections.singletonList(ticketFlowChartEntity), user);
    }

    /**
     * 处理流转企业
     *
     * @param circulationEnterpriseList 流转企业集合
     * @param ticketOpenEntity          申请id
     */
    private void setCirculationEnterprise(List<CirculationEnterpriseDto> circulationEnterpriseList, TicketOpenEntity ticketOpenEntity) {
        if (!CollectionUtils.isEmpty(circulationEnterpriseList)) {
            List<TicketCirculationEnterpriseEntity> list = new ArrayList<>();
            circulationEnterpriseList.forEach(x -> {
                TicketCirculationEnterpriseEntity ticketCirculationEnterpriseEntity = new TicketCirculationEnterpriseEntity();
                BeanUtils.copyProperties(x, ticketCirculationEnterpriseEntity);
                ticketCirculationEnterpriseEntity.setTicketOpenId(ticketOpenEntity.getId());
                ticketCirculationEnterpriseEntity.setCreatorId(ticketOpenEntity.getCreatorId());
                ticketCirculationEnterpriseEntity.setUpdaterId(ticketOpenEntity.getCreatorId());
                ticketCirculationEnterpriseEntity.setEnterpriseId(ticketOpenEntity.getEnterpriseId());
                list.add(ticketCirculationEnterpriseEntity);
            });


            ticketCirculationEnterpriseService.saveBatch(list);
        }


    }

    /**
     * 保存开票资料
     *
     * @param fileList 资料集合
     * @param user     用户
     * @param entity   开立实体
     * @throws SysException 异常
     */
    private void saveTicketOpenData(List<TicketDataDto> fileList, User user, TicketOpenEntity entity) throws SysException {
        if (!CollectionUtils.isEmpty(fileList)) {
            fileList.forEach(x -> {
                x.setTicketActionId(entity.getId());
                x.setTicketActionType(TicketActionTypeEnum.TICKET_TYPE_OPEN.getCode());
            });
            ticketDataService.saveTicketData(fileList, user);
        }
    }

    /**
     * 获取上链地址
     *
     * @return
     */
    private String getChainAddress() {
        return UuidUtils.getUuid();
    }

    private List<Integer> getMsgUserListByFeign(String menuName, String enterpriseId) throws SysException {
        // 获取复核人
        List<Integer> userList = userFeign.getUserIdListByButtonAction(menuName, enterpriseId);
        if (CollectionUtils.isEmpty(userList)) {
            throw new SysException("用户中心：未找到 " + menuName + "权限的 用户");
        }
        return userList;
    }

    /**
     * 处理 供应商授信
     *
     * @param entity 开立实体
     * @param user   当前登录用户
     * @throws SysException 业务异常
     */
    private void handSupplierCredit(TicketOpenEntity entity, User user) throws SysException {
        QueryWrapper<SupplierCreditEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
//                .eq(SupplierCreditEntity::getCreditUseType, CreditUseTypeEnum.OPEN_CREDIT.getCode())
                .eq(SupplierCreditEntity::getLatestStatus, CreditDataStatusEnum.LATEST.getCode())
                .eq(SupplierCreditEntity::getReceiveEnterpriseId, entity.getReceiveEnterpriseId())
                .eq(SupplierCreditEntity::getInstitutyId, entity.getInstinctyId());
        List<SupplierCreditEntity> list = supplierCreditService.list(queryWrapper);
        if (CollectionUtils.isEmpty(list)) {
            throw new SysException("未找到当前的供应商的 开立额度");
        }
        if (list.size() > 1) {
            throw new SysException("供应商在金融机构的开立额度存在多个");
        }
        SupplierCreditEntity supplierCredit = list.get(0);
        Double availableAmount = supplierCredit.getCreditAvailableAmount();
        if (availableAmount < entity.getTicketAmount()) {
            throw new SysException("当前供应商开立额度不足");
        }
        // 更新供应商的 可用开开票额度
        supplierCredit.setCreditAvailableAmount(supplierCredit.getCreditAvailableAmount() - entity.getTicketAmount());
        supplierCredit.setCreditFrozenAmount(supplierCredit.getCreditFrozenAmount() + entity.getTicketAmount());
        supplierCredit.setUpdateTime(new Date());
        supplierCredit.setUpdaterId(user.getId());
        supplierCreditService.updateById(supplierCredit);
    }

    /**
     * 分页查询开单待复核
     *
     * @param params 查询参数
     * @return 分页数据
     */
    @Override
    public PageUtils queryOpenChekPage(Map<String, Object> params, User user) throws SysException {
        /*TODO*/
        return null;
    }

    /**
     * 分页查询开单待平台审批
     *
     * @param params 查询参数
     * @return 分页数据
     */
    @Override
    public PageUtils queryOpenApprovePage(Map<String, Object> params) {
        //排序字段：create_time
        String pageField = (String) params.get(Constant.PAGE);
        //排序方式 order:  desc asc
        String order = (String) params.get(Constant.ORDER);
        IPage<TicketOpenCheckPage> page = new Query<TicketOpenCheckPage>()
                .getPage(params, StringUtils.isBlank(pageField)
                        ? StringConstant.CREATE_TIME_FILED : pageField, Constant.ASC.equals(order));
        ticketCommonService.changeTenThousand2Number(params, "startAmount");
        ticketCommonService.changeTenThousand2Number(params, "endAmount");

        IPage<TicketOpenCheckPage> openPage = this.baseMapper.queryOpenApprovePage(page, params);
        List<TicketOpenCheckPage> checkPageRecords = openPage.getRecords();
        if (!CollectionUtils.isEmpty(checkPageRecords)) {
            for (TicketOpenCheckPage openPageRecord : checkPageRecords) {
                openPageRecord.setOperateButton(Collections.singletonList(new KeyValue(OperateButtonEnum.APPROVE_FINANCE.getCode(), OperateButtonEnum.APPROVE_FINANCE.getValue())));
            }
        }
        return new PageUtils(openPage);
    }

    /**
     * 开立复核提交
     * 1、记录申请的操作记录
     * 2、调用流程去审核接口
     *
     * @param checkSubmit 复核提交参数
     * @param user        登录用户
     * @throws SysException 业务异常
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void openTicketCheckSubmit(ApprovalSubmitDto checkSubmit, User user) throws SysException {
        /*TODO*/
    }

    /**
     * 发送开立票据审核消息
     *
     * @param checkSubmit 审核信息对象
     * @param openEntity  开立票据对象
     * @param user        登录用户
     */
    private void sendOpenTicketCheckMsg(ApprovalSubmitDto checkSubmit, TicketOpenEntity openEntity, User user) throws SysException {
        //未通过
        if (ApproveResultStatusEnum.APPROVE_STATUS_NO.getCode().equals(checkSubmit.getApprovalResult())) {
            // 核心企业 额度回滚
            coreEnterpriseCreditService.rollBackCoreCredit(openEntity.getCoreEnterpriseCreditId(), openEntity.getTicketAmount(), user);
            //一个是 cc 抄送人得消息 一个是下个节点的消息
            checkSubmit.setCcMsg(JSON.toJSONString(new MsgInfoDto(openEntity.getId(), GotoTypeEnum.GOTO_TYPE_OPEN.getCode().toString()
                    , mailMessageService.getMsgInfo(openEntity, MailMessageConstant.TICKET_OPEN_FAIL_CC_MSG), "")));
        } else {
            checkSubmit.setCcMsg(JSON.toJSONString(new MsgInfoDto(openEntity.getId(), GotoTypeEnum.GOTO_TYPE_OPEN.getCode().toString()
                    , mailMessageService.getMsgInfo(openEntity, MailMessageConstant.TICKET_OPEN_PASS_CC_MSG), "")));
            checkSubmit.setMsg(JSON.toJSONString(new MsgInfoDto(openEntity.getId(), GotoTypeEnum.GOTO_TYPE_OPEN.getCode().toString()
                    , mailMessageService.getMsgInfo(openEntity, MailMessageConstant.TICKET_OPEN_APPROVE), "")));
        }
    }


    /**
     * 开单复核发送消息
     * 复核未通过-申请人(抄送人消息在流程处理)
     * 复核通过
     *
     * @param openEntity
     */
    private void sendOpenReviewMsg(TicketOpenEntity openEntity, User user) throws SysException {
        //申请人消息+抄送人消息
        List<BathMsgDto> bathMsgDtoList1 = mailMessageService.setBatchTicketOpenMsg(openEntity, user);
        sendMsgService.sendBathMsgToMq(bathMsgDtoList1);
    }

    /**
     * 开单 平台审批的提交
     *
     * @param checkSubmit 审批结果
     * @param user        登录用户
     * @throws SysException 业务异常
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void openTicketApproveSubmit(ApprovalSubmitDto checkSubmit, User user) throws SysException {
        verifyCheckSubmit(checkSubmit);
        TicketOpenEntity openEntity = this.getById(checkSubmit.getId());
        if (ObjectUtils.isEmpty(openEntity)) {
            throw new SysException("当前凭证不存在");
        }
        if (openEntity.getAdvancePaymentStatus().equals(StringConstant.YES)) {
            throw new SysException("当前凭证已经进入预兑付");
        }
        if (!openEntity.getTicketStatus().equals(TicketStatusEnum.OPEN_PENDING_APPROVE.getCode())) {
            throw new SysException("当前凭证未处于审批状态");
        }
        openEntity.setPlatformCheckRemark(checkSubmit.getApprovalRemark());
        openEntity.setPlatApprovalId(user.getId());
        openEntity.setPlatApprovalTime(new Date());
        if (checkSubmit.getApprovalResult().equals(StringConstant.NO)) {
            openEntity.setTicketStatus(TicketStatusEnum.APPROVE_REJECT.getCode());
            openEntity.setApproveStatus(ApproveStatusEnum.PLATFORM_REJECT.getCode());
            // 开立审批操作记录提交
//            operateLogService.saveOpenTicketLog(openEntity, TicketStatusEnum.OPEN_PENDING_SIGN.getCode(), OperateLogTypeEnum.APPROVAL_REJECTION.getCode(), user);
            // 供应商额度回滚
            supplierCreditService.rollBackSupplierCredit(openEntity.getReceiveEnterpriseId(), openEntity.getTicketAmount(), openEntity.getInstinctyId(), user);
            // 核心企业 额度回滚
            coreEnterpriseCreditService.rollBackCoreCredit(openEntity.getCoreEnterpriseCreditId(), openEntity.getTicketAmount(), user);
            // 发送消息
            BusinessMsg businessMsg = new BusinessMsg(null, openEntity.getOpenEnterpriseName(), openEntity.getReceiveEnterpriseName(), openEntity.getTicketAmount());
//            String ticketOpenMsg = sendMessageManager.getTicketOpenMsg(7, businessMsg);
            // 申请人 与 复核人
//            sendMsgService.sendMsgToMq(MsgCategoryEnum.MSG_CATEGORY_ORDINARY.getCode(), ticketOpenMsg, Arrays.asList(openEntity.getCreatorId(), null), user, null);
        } else {
            openEntity.setTicketStatus(TicketStatusEnum.OPEN_PENDING_SIGN.getCode());
            openEntity.setApproveStatus(ApproveStatusEnum.PLATFORM_PASS.getCode());
            // 增加 平台操作记录
//            operateLogService.saveOpenTicketLog(openEntity, TicketStatusEnum.OPEN_PENDING_APPROVE.getCode(), OperateLogTypeEnum.APPROVE_PASS.getCode(), user);
            openEntity.setPlatformCheckRemark(openEntity.getPlatformCheckRemark());
            // 发送信息
            BusinessMsg businessMsg = new BusinessMsg(null, openEntity.getOpenEnterpriseName(), openEntity.getReceiveEnterpriseName(), openEntity.getTicketAmount());
//            String ticketOpenMsg = sendMessageManager.getTicketOpenMsg(6, businessMsg);
//            sendMsgService.sendMsgToMq(MsgCategoryEnum.MSG_CATEGORY_ORDINARY.getCode(), ticketOpenMsg
//                    , Arrays.asList(openEntity.getCreatorId(), null), user, null);
        }
        this.updateById(openEntity);
    }


    /**
     * 开立签收的 提交
     *
     * @param checkSubmit 提交对象
     * @param user        登录用户
     * @throws SysException 业务异常
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void openTicketSignSubmit(ApprovalSubmitDto checkSubmit, User user) throws SysException {
        log.info("开立签收--签收结果提交{}", JSON.toJSONString(checkSubmit));
        verifyCheckSubmit(checkSubmit);
        // 1. 验证当前的票据状态 2. 验证协议的签署状态
        // 3. 更改票的状态 审批状态
        TicketOpenEntity openEntity = this.getById(checkSubmit.getId());
        if (ObjectUtils.isEmpty(openEntity)) {
            throw new SysException("当前凭证不存在");
        }
        if (openEntity.getAdvancePaymentStatus().equals(StringConstant.YES)) {
            throw new SysException("当前凭证已经进入预兑付");
        }
        if (!openEntity.getTicketStatus().equals(TicketStatusEnum.OPEN_PENDING_SIGN.getCode())) {
            throw new SysException("当前凭证已签收");
        }
        //拼接发送消息
        QueryWrapper<TicketProcessEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(TicketProcessEntity::getTicketOpenId, openEntity.getId())
                .eq(TicketProcessEntity::getApproveType, ApproveTypeEnum.SIGN_OPEN_TRANSFER.getCode());
        TicketProcessEntity processEntity = ticketProcessService.getOne(queryWrapper);
        if (null == processEntity) {
            throw new SysException("审批流程不存在");
        }
        checkSubmit.setApprovalId(processEntity.getApproveId());


        // 如果 转让的票被锁定则 不用发 rpc
        TicketOpenEntity entity = this.getById(checkSubmit.getId());
        if (ObjectUtils.isEmpty(entity)) {
            throw new SysException("未找到当前开立申请");
        }
        // 拼接消息
        String msg = MailMessageConstant.TICKET_OPEN_SIGN_APPROVE.replace(MailMessageConstant.TICKET_REPLACE_OPEN_TICKET, entity.getOpenEnterpriseName())
                .replace(MailMessageConstant.TICKET_REPLACE_MONEY, NumberUtil.numberFormatString(entity.getTicketAmount()));
        MsgInfoDto infoDto = new MsgInfoDto(entity.getId(), GotoTypeEnum.GOTO_TYPE_SIGN_OPEN.getCode().toString()
                , msg, "");
        checkSubmit.setMsg(JSON.toJSONString(infoDto));

        // 拼接抄送消息
        String ccMsg = MailMessageConstant.TICKET_OPEN_SIGN_APPROVE_CC.replace(MailMessageConstant.TICKET_REPLACE_OPEN_TICKET, entity.getOpenEnterpriseName())
                .replace(MailMessageConstant.TICKET_REPLACE_MONEY, NumberUtil.numberFormatString(entity.getTicketAmount()));
        MsgInfoDto ccMsginfoDto = new MsgInfoDto(entity.getId(), GotoTypeEnum.GOTO_TYPE_SIGN_OPEN.getCode().toString()
                , ccMsg, "");
        checkSubmit.setCcMsg(JSON.toJSONString(ccMsginfoDto));
        checkSubmit.setApprovalType(ApproveTypeEnum.SIGN_OPEN_TRANSFER.getCode());
        checkSubmit.setApplyType("0");
        Result result = processFeign.processSubmit(checkSubmit);
        if (!ObjectUtils.isEmpty(result) && ResultCode.SUCCESS.getCode() != result.getCode()) {
            throw new SysException(result.getMsg());
        }
    }

    /**
     * 复核提交对象 验证
     *
     * @param checkSubmit 审批对象
     * @throws SysException 业务异常
     */
    private void verifyCheckSubmit(ApprovalSubmitDto checkSubmit) throws SysException {
        if (checkSubmit.getApprovalResult().equals(StringConstant.NO) && ObjectUtils.isEmpty(checkSubmit.getApprovalRemark())) {
            throw new SysException("审批驳回时，备注不能为空");
        }
    }

    /**
     * 开单签收接口
     *
     * @param params 参数
     * @param user   用户
     * @return 分页数据v
     * @throws SysException 业务异常
     */
    @Override
    public PageUtils queryOpenSignPage(Map<String, Object> params, User user) throws SysException {
        params.put("enterpriseId", user.getEnterpriseId());
        //排序字段：create_time
        String pageField = (String) params.get(Constant.PAGE);
        //排序方式 order:  desc asc
        String order = (String) params.get(Constant.ORDER);
        IPage<TicketOpenSignPage> page = new Query<TicketOpenSignPage>()
                .getPage(params, StringUtils.isBlank(pageField)
                        ? StringConstant.CREATE_TIME_FILED : pageField, Constant.ASC.equals(order));
        ticketCommonService.changeTenThousand2Number(params, "startAmount");
        ticketCommonService.changeTenThousand2Number(params, "endAmount");
        params.computeIfPresent("handStatus", (k, v) -> TicketStatusEnum.getHandStatusList(Integer.valueOf(v.toString())));
/**
 * todo 测试暂时不开启
 */
//        //查看当前用户有签收流程权限的开立申请id
//        JSONObject processParams = new JSONObject();
//        processParams.put("userId", user.getId());
//        processParams.put("approveTypeCode", ApproveTypeEnum.SIGN_OPEN.getCode());
//        List<String> openTicketIds = processFeign.getByReviewer(processParams);
//        if (CollectionUtils.isEmpty(openTicketIds)) {
//            return new PageUtils(page);
//        }
//
//        params.put("ids", openTicketIds);
        IPage<TicketOpenSignPage> openPage = this.baseMapper.queryOpenSingPage(page, params);
        List<TicketOpenSignPage> pageRecords = openPage.getRecords();
        if (!CollectionUtils.isEmpty(pageRecords)) {
            ticketSignHandStatusService.setTicketSignOperates(pageRecords);
            for (TicketOpenSignPage pageRecord : pageRecords) {
                String s = ticketCommonService.calculateSurplusDays(pageRecord.getExpireTime(), pageRecord.getTicketStatus());
                pageRecord.setSurplusCashingDays(s);
            }
        }
        return new PageUtils(openPage);
    }


    /**
     * 撤销开单
     *
     * @param id   主键id
     * @param user 登录用户
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelOpenTicket(String id, User user) throws SysException {
        log.info("撤销开单{}", id);
        TicketOpenEntity entity = this.getById(id);
        Integer preStatus = entity.getTicketStatus();
        if (ObjectUtils.isEmpty(entity)) {
            throw new SysException("未找到当前票据");
        }
        if (!TicketStatusEnum.OPEN_PENDING_REVIEW.getCode().equals(entity.getTicketStatus())) {
            throw new SysException("当前票据不是内部复核状态，不可撤回");
        }
        entity.setTicketStatus(TicketStatusEnum.CANCEL.getCode());
        entity.setUpdaterId(user.getId());
        entity.setUpdateTime(new Date());
        this.updateById(entity);
        // 核心企业 授信回滚
        coreEnterpriseCreditService.rollBackCoreCredit(entity.getCoreEnterpriseCreditId(), entity.getTicketAmount(), user);
        // 供应商额度回滚
//        supplierCreditService.rollBackSupplierCredit(entity.getReceiveEnterpriseId(), entity.getTicketAmount(), entity.getInstinctyId(), user);

        //撤回时，流转轨迹改为失效
        TicketFlowChartEntity ticketFlowChart = ticketFlowChartService.getOneByOpenTicketId(id);
        ticketFlowChart.setTicketStatus(FlowChartColourEnum.GREY.getCode());
        ticketFlowChartService.updateById(ticketFlowChart);
        // 新增操作记录
        ticketOperateLogService.saveTicketLog(LogActionTypeEnum.TICKET_TYPE_OPEN.getCode(), entity.getId()
                , TicketLogStatusEnum.OPEN_PENDING_REVIEW.getCode(), OperateLogTypeEnum.WITHDRAW.getCode(), TicketLogStatusEnum.OPEN_PENDING_APPROVE.getCode(), user
                , entity.getRemark(),
                entity.getReceiveEnterpriseId(), entity.getReceiveEnterpriseName(),
                entity.getOpenEnterpriseId(), entity.getOpenEnterpriseName(), "", "0", null, null);


    }

    /**
     * 重新申请
     * 重新申请时新生成一笔单据
     *
     * @param id   开票id
     * @param user 用户
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void reapplyOpenTicket(String id, User user) throws SysException {
        TicketOpenEntity byId = this.getById(id);
        if (!TicketStatusEnum.REJECT_LIST.contains(byId.getTicketStatus())) {
            throw new SysException("只有处理状态是失败的开立申请才能发起重新申请，当前申请状态为" + TicketStatusEnum.getStatusName(byId.getTicketStatus()));
        }
        TicketOpenAddDto ticketOpenAddDto = new TicketOpenAddDto();
        BeanUtils.copyProperties(byId, ticketOpenAddDto);
        ticketOpenAddDto.setInvoiceTime(new Date());
        this.saveTicketOpen(ticketOpenAddDto, user);
        // 找到开立相关附件信息
        List<TicketDataEntity> dataEntities = ticketDataService.getByTicketId(id, TicketActionTypeEnum.TICKET_TYPE_OPEN.getCode());
        if (!CollectionUtils.isEmpty(dataEntities)) {
            List<TicketDataDto> proofList = new ArrayList<>();
            List<TicketDataDto> ticketDataDtos = QmBeanUtils.copyListProperties(proofList, TicketDataDto::new);
            Map<String, List<TicketDataDto>> map = ticketDataDtos.stream().collect(Collectors.groupingBy(TicketDataDto::getDataType));
            ticketOpenAddDto.setInvoiceList(map.get(DataTypeEnum.INVOICE.getCode()));
            ticketOpenAddDto.setProofList(map.get(DataTypeEnum.TICKET.getCode()));
        }
    }

    /**
     * 开立的流程进度
     *
     * @param id 开立id
     * @return 流程进度集合
     */
    @Override
    public List<ApUserShowInfo> getOpenProcessProgress(String id) throws SysException {
        /*TODO*/
        return new ArrayList<>();
    }

    /**
     * 核心企业概况-还款计划
     *
     * @param user 登录用户
     * @return 还款计划列表
     */
    @Override
    public PageUtils repayPlan(Map<String, Object> params, User user) throws SysException {

        //状态为已签收的
        QueryWrapper<TicketOpenEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(TicketOpenEntity::getTicketStatus, TicketStatusEnum.NORMAL_HOLDING.getCode());
        //核心企业查询当前用户的，集团查询所有的
        if (UserCategoryConstant.USER_CATEGORY_ENTERPRISE.getKey().equals(user.getUserCategory())) {
            queryWrapper.lambda().eq(TicketOpenEntity::getEnterpriseId, user.getEnterpriseId());
        }
        IPage<TicketOpenEntity> page = this.page(
                new Query<TicketOpenEntity>().getPage(params, StringConstant.EXPIRE_TIME, Boolean.FALSE),
                queryWrapper
        );

        return new PageUtils(page);
    }

    /**
     * 回滚 开立以及开立签收的数据
     * 1. 开立申请 终止
     * 2. 授信一个时集团的综合授信
     * 3. 供应商的开票额度
     *
     * @param days 天数 周期
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized void executeWaitingPayment(Integer days) throws SysException {
        log.info("开立预兑付的回滚,入参：{}", days);
        List<TicketOpenEntity> openEntities = getAdvanceOpenTicket(days);
        if (CollectionUtils.isEmpty(openEntities)) {
            log.warn("预兑付，回滚开立以及开立签收的数据，开立申请单的id数组为空");
            return;
        }
        // 更新 开立申请表的状态
        List<String> openTicketIds = openEntities.stream().map(TicketOpenEntity::getId).distinct().collect(Collectors.toList());
        UpdateWrapper<TicketOpenEntity> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().in(TicketOpenEntity::getId, openTicketIds)
                .set(TicketOpenEntity::getAdvancePaymentStatus, AdvancePaymentStatusEnum.COULD.getCode())
                .set(TicketOpenEntity::getTicketStatus, TicketStatusEnum.WAITING_PAYMENT.getCode())
                .set(TicketOpenEntity::getCashStatus, TicketCashingEnum.WAITING_FOR_PAYMENT.getCode())
                .set(TicketOpenEntity::getUpdateTime, new Date());
        this.update(updateWrapper);
        for (TicketOpenEntity openEntity : openEntities) {
            // 企业授信的 回滚
            coreEnterpriseCreditService.rollBackCoreCredit(openEntity.getCoreEnterpriseCreditId(), openEntity.getTicketAmount(), new User());
            // 供应商开票额度的 回滚
            supplierCreditService.rollBackSupplierCredit(openEntity.getReceiveEnterpriseId(), openEntity.getTicketAmount(), openEntity.getInstinctyId(), new User());
        }
    }

    @Override
    public IPage<CashTicketPage> cashingPage(IPage<CashTicketPage> page, Map<String, Object> params) {
        return baseMapper.cashingPage(page, params);
    }

    /**
     * 计算规则
     * 设： x= 当前日期 + days 取日期
     * 开立申请表的所有日期 取日期 小于等于 x 此为条件1
     *
     * @param days 时间周期
     * @return 进入兑付日期的 票
     */
    private List<TicketOpenEntity> getAdvanceOpenTicket(Integer days) {
        // 预兑付 all in 所有的数据计算
        Date cashTime = com.zzl.bonds.ticket.untils.DateUtils.dateAddDaysAll(days);
        if (ObjectUtils.isEmpty(cashTime)) {
            return new ArrayList<>();
        }
        QueryWrapper<TicketOpenEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().ne(TicketOpenEntity::getAdvancePaymentStatus, AdvancePaymentStatusEnum.COULD)
                .le(TicketOpenEntity::getCashTime, cashTime);

        List<TicketOpenEntity> list = this.list(queryWrapper);
        if (CollectionUtils.isEmpty(list)) {
            return new ArrayList<>();
        }
        return list;
    }

    /**
     * 审批流程结束通知
     *
     * @param noticeApprovalResult 审批信息
     * @param user                 登录用户
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void approveEndNotice(NoticeApprovalResult noticeApprovalResult, User user) throws SysException {
        //1 查询当前流程
        QueryWrapper<TicketProcessEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(TicketProcessEntity::getApproveId, noticeApprovalResult.getApprovalId());
        List<TicketProcessEntity> list = ticketProcessService.list(queryWrapper);
        //参数校验
        TicketProcessEntity ticketProcessEntity = checkTicketProcessEntity(list);
        //开立申请
        if (ApproveTypeEnum.OPEN_EXAMING.getCode().equals(ticketProcessEntity.getApproveType())) {
            this.ticketOpenCallback(noticeApprovalResult, ticketProcessEntity, user);
        }
        //开立签收回调
        else if (ApproveTypeEnum.SIGN_OPEN_TRANSFER.getCode().equals(ticketProcessEntity.getApproveType())) {
            this.ticketOpenSignCallback(noticeApprovalResult, ticketProcessEntity, user);

            //转让签收回调
        } else if (ApproveTypeEnum.SIGN_TRANSFER.getCode().equals(ticketProcessEntity.getApproveType())) {
            ticketTransferService.transferSignInApprovalCallback(noticeApprovalResult, ticketProcessEntity, user);
        }
        //转让
        else if (ApproveTypeEnum.TRANSFER.getCode().equals(ticketProcessEntity.getApproveType())) {
            ticketTransferService.transferReviewCallback(noticeApprovalResult, ticketProcessEntity, user);
        }
        //融资
        else if (ApproveTypeEnum.FINANCE.getCode().equals(ticketProcessEntity.getApproveType())) {
            ticketFinanceService.ticketWithdrawCallback(noticeApprovalResult, ticketProcessEntity, user);
        }
    }

    /**
     * 开立审批，流程的回调函数
     * 1、修改流程状态
     * 2、修改票据状态
     * 2.1 通过-拉起签收流程
     * 2.2 通过-创建协议
     * 2.3 通过-操作日志
     * 3、发消息通知
     *
     * @param noticeApprovalResult 回调参数
     * @param ticketProcessEntity  流程数据
     * @param user                 登录用户
     */
    private void ticketOpenCallback(NoticeApprovalResult noticeApprovalResult, TicketProcessEntity ticketProcessEntity, User user) throws SysException {
        TicketOpenEntity ticketOpenEntity = baseMapper.selectById(ticketProcessEntity.getTicketOpenId());
        if (ObjectUtils.isEmpty(ticketOpenEntity)) {
            throw new SysException("未找到当前开立申请");
        }
        //开立复核被驳回
        if (ApproveResultStatusEnum.APPROVE_STATUS_NO.getCode().equals(noticeApprovalResult.getApprovalResult())) {
            ticketOpenEntity.setTicketStatus(TicketStatusEnum.REVIEW_REJECT.getCode());
            ticketOpenEntity.setApproveStatus(ApproveStatusEnum.PARENT_ENTERPRISE_REJECT.getCode());
            ticketProcessEntity.setApproveStatus(ApproveProcessEnum.APPROVE_PROCESS_FAIL.getCode());
            //失败时，流转轨迹改为失效
            TicketFlowChartEntity ticketFlowChart = ticketFlowChartService.getOneByOpenTicketId(ticketProcessEntity.getTicketOpenId());
            ticketFlowChart.setTicketStatus(FlowChartColourEnum.GREY.getCode());
            ticketFlowChartService.updateById(ticketFlowChart);

        } else {
            //开立复核审批通过
            ticketProcessEntity.setApproveStatus(ApproveProcessEnum.APPROVE_PROCESS_SUCESS.getCode());
            //todo 暂时设为待签收
            ticketOpenEntity.setTicketStatus(TicketStatusEnum.OPEN_PENDING_SIGN.getCode());
            ticketOpenEntity.setApproveStatus(ApproveStatusEnum.PARENT_ENTERPRISE_PASS.getCode());
//            2.1 通过-拉起签收流程
            //开始开立签收流程
            MsgInfoDto msgInfoDto = mailMessageService.setTicketOpenMsg(ticketOpenEntity, user);
            ticketProcessService.saveTicketProcess(ticketOpenEntity.getTicketAmount()
                    , ticketOpenEntity.getId(), JSON.toJSONString(msgInfoDto), ApproveTypeEnum.SIGN_OPEN_TRANSFER, user, ticketOpenEntity.getReceiveEnterpriseId()
                    , TicketActionTypeEnum.TICKET_TYPE_OPEN.getCode().toString());
//            2.2 通过-创建协议
//            ticketFinanceService.setBusinessProtocol(ticketOpenEntity.getId(), ContractTicketTypeEnum.TICKET_TYPE_OPEN_SIGN.getCode());
//            2.3 通过-操作日志
            ticketOperateLogService.saveTicketLog(LogActionTypeEnum.TICKET_TYPE_OPEN.getCode(), ticketOpenEntity.getId()
                    , TicketLogStatusEnum.OPEN_PENDING_REVIEW.getCode(), OperateLogTypeEnum.REVIEW_PASS.getCode(), TicketLogStatusEnum.OPEN_PENDING_APPROVE.getCode(), user
                    , noticeApprovalResult.getApprovalRemark(),
                    ticketOpenEntity.getReceiveEnterpriseId(), ticketOpenEntity.getReceiveEnterpriseName(),
                    ticketOpenEntity.getOpenEnterpriseId(), ticketOpenEntity.getOpenEnterpriseName(), "", "0", null, null, noticeApprovalResult);

        }
        //2、修改票据状态
        this.updateById(ticketOpenEntity);
        // 开立复核通过/不通过时，发送消息
        this.sendOpenReviewMsg(ticketOpenEntity, user);
        //1、修改流程状态
        ticketProcessService.updateById(ticketProcessEntity);


    }


    /**
     * 校验
     *
     * @param list 开立申请集合
     * @return
     * @throws SysException 异常
     */
    private TicketProcessEntity checkTicketProcessEntity(List<TicketProcessEntity> list) throws SysException {
        // 申请审批数据为空
        if (CollectionUtils.isEmpty(list)) {
            throw new SysException("当前审批流程不存在");
        }
        TicketProcessEntity ticketProcessEntity = list.get(0);
        //校验当前流程是否已完成
        if (!ApproveProcessEnum.APPROVE_PROCESS_ING.getCode().equals(ticketProcessEntity.getApproveStatus())) {
            throw new SysException("当前审批流程已处理，请勿重复审批");
        }
        return ticketProcessEntity;
    }

    /**
     * 开立签收
     * 通过：
     * 1、修改签收状态，已签收
     * 2、修改签收审批流
     * 3、添加我的票据
     * 4、授信扣减
     * 驳回：
     * 1、修改签收状态，
     * 2、修改签收审批流
     * 3、授信恢复
     *
     * @param noticeApprovalResult 审批结果
     * @param ticketProcessEntity  开立流程
     * @param user                 登录用户
     * @throws SysException 业务异常
     */
    private void ticketOpenSignCallback(NoticeApprovalResult noticeApprovalResult, TicketProcessEntity ticketProcessEntity, User user) throws SysException {
        //查询申请人信息
        TicketOpenEntity oldOpenEntity = this.getById(ticketProcessEntity.getTicketOpenId());
        //开立签收时，获取开立审批人需要发送消息的集合
        UserEntity userDto = getOpenApproveUser(noticeApprovalResult, oldOpenEntity);
        //审批通过：
        if (ApproveResultStatusEnum.APPROVE_STATUS_YES.getCode().equals(noticeApprovalResult.getApprovalResult())) {
            this.passTicketOpenSign(oldOpenEntity, userDto, noticeApprovalResult, ticketProcessEntity, user);
        } else if (ApproveResultStatusEnum.APPROVE_STATUS_NO.getCode().equals(noticeApprovalResult.getApprovalResult())) {
            //驳回：
            this.rejectTicketOpenSign(oldOpenEntity, userDto, noticeApprovalResult, ticketProcessEntity, user);
        } else {
            throw new SysException("审批结果不正确");
        }
    }

    /**
     * * 驳回：
     * * 1、修改签收状态，已签收
     * * 2、修改签收审批流
     * * 3、授信恢复
     *
     * @param noticeApprovalResult 审批结果
     * @param ticketProcessEntity  开立流程
     * @param user                 登录用户
     * @throws SysException 业务异常
     */
    private void rejectTicketOpenSign(TicketOpenEntity oldOpenEntity, UserEntity userDto, NoticeApprovalResult noticeApprovalResult, TicketProcessEntity ticketProcessEntity, User user) throws SysException {
        // 修改签收状态，已签收
        String ticketOpenId = ticketProcessEntity.getTicketOpenId();
        TicketOpenEntity ticketOpenEntity = new TicketOpenEntity();
        ticketOpenEntity.setId(ticketOpenId);
        ticketOpenEntity.setApproveStatus(ApproveStatusEnum.RECEIVE_ENTERPRISE_REJECT.getCode());
        ticketOpenEntity.setTicketStatus(TicketStatusEnum.REFUSE.getCode());
        this.updateById(ticketOpenEntity);
        //修改签收审批流
        TicketProcessEntity processEntity = new TicketProcessEntity();
        processEntity.setApproveStatus(ApproveProcessEnum.APPROVE_PROCESS_FAIL.getCode());
        processEntity.setRemark(noticeApprovalResult.getApprovalRemark());
        processEntity.setId(ticketProcessEntity.getId());
        processEntity.setUpdaterId(user.getId());
        ticketProcessService.updateById(processEntity);
        //授信扣减
//        institutyCreditService.processCredit(this.getById(ticketId), CreditStatusProcessEnum.CREDIT_RECOVER.getCode(), null);
        //签收审核不通过，给复核人和申请人 和抄送人 发站内信
        this.sendOpenMsg(oldOpenEntity, noticeApprovalResult.getBeforeReceiveUserIds(), userDto, MailMessageConstant.TICKET_OPEN_SIGN_FAIL_MSG);
        //签收审核不通过，给签收审核人员发站内信  todo 待处理
        //todo 获取平台拥有审批权限的用户ids
        List<String> platIds = new ArrayList<>();
        noticeApprovalResult.setReceiveUserIds(platIds);
        //发送签收消息
        this.sendSignMsg(oldOpenEntity, noticeApprovalResult, user, MailMessageConstant.SUPPLIER_SIGN_FAIL_MSG);
        //保存操作日志
        ticketOperateLogService.saveTicketLog(LogActionTypeEnum.TICKET_TYPE_OPEN.getCode(), oldOpenEntity.getId()
                , TicketLogStatusEnum.OPEN_PENDING_SIGN.getCode(), OperateLogTypeEnum.SIGN.getCode(), TicketLogStatusEnum.REVIEW_REJECT.getCode(), user
                , noticeApprovalResult.getApprovalRemark(),
                oldOpenEntity.getReceiveEnterpriseId(), oldOpenEntity.getReceiveEnterpriseName(),
                oldOpenEntity.getOpenEnterpriseId(), oldOpenEntity.getOpenEnterpriseName(), "", "0", null, null);
        this.saveOpenSignFlowChart(oldOpenEntity, "3", "");
    }

    /**
     * 开立签收，给签收方审核人发消息
     *
     * @param oldOpenEntity        开立申请
     * @param noticeApprovalResult 回调参数
     * @param user                 登录用户
     * @param msgContent           消息内容
     * @throws SysException 异常审核
     */
    private void sendSignMsg(TicketOpenEntity oldOpenEntity, NoticeApprovalResult noticeApprovalResult, User user, String msgContent) throws SysException {
        String msg = msgContent.replace("{{申请方}}", oldOpenEntity.getOpenEnterpriseName())
                .replace("{{金额}}", NumberUtil.numberFormatString(oldOpenEntity.getTicketAmount())
                        .replace("{{签收方}}", oldOpenEntity.getReceiveEnterpriseName()));
        //所有审批人
        //若是默认节点，则发送给系统所有人
        if (noticeApprovalResult.getIsNoticeAllUser()) {
            //根据企业id查询根部门
            if (null == oldOpenEntity.getReceiveEnterpriseId()) {
                throw new SysException("发送消息给系统所有人，企业id不能为空");
            }
            noticeApprovalResult.setEnterpriseId(oldOpenEntity.getReceiveEnterpriseId());
            List<String> orgIdByEnterpriseId = userFeign.getOrgIdListByEnterpriseId(noticeApprovalResult.getEnterpriseId());
            noticeApprovalResult.setReceiveOrgId(orgIdByEnterpriseId);
        }
        //消息通知
        sendMsgService.sendMsgToAllNotifier(oldOpenEntity.getId(), noticeApprovalResult.getReceiveUserIds()
                , GotoTypeEnum.GOTO_TYPE_OPEN.getCode().toString(), msg, user, noticeApprovalResult.getReceiveOrgId());
    }

    /**
     * 开立签收审批人处理
     *
     * @param oldOpenEntity 开立申请
     * @return userDto       用户信息
     * @throws SysException 业务异常
     */
    private UserEntity getOpenApproveUser(NoticeApprovalResult noticeApprovalResult, TicketOpenEntity oldOpenEntity) throws SysException {
        //1 查询开立签收对应的开立流程
        QueryWrapper<TicketProcessEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().in(TicketProcessEntity::getApproveType, Arrays.asList(ApproveTypeEnum.SIGN_OPEN_TRANSFER.getCode(),
                ApproveTypeEnum.OPEN_EXAMING.getCode()))
                .eq(TicketProcessEntity::getTicketOpenId, oldOpenEntity.getId());
        List<TicketProcessEntity> list = ticketProcessService.list(queryWrapper);
        if (CollectionUtils.isEmpty(list)) {
            return new UserEntity();
        }
        //2 调用rpc，开立签收审核结束后，查询开立的所有审核人id集合
        List<String> collect = list.stream().map(TicketProcessEntity::getApproveId).collect(Collectors.toList());
        List<String> userIds = processFeign.getAllUserByApprovalId(new StringIds(collect));
        noticeApprovalResult.setBeforeReceiveUserIds(userIds);
        //3 查询申请人
        Result userInfo = userFeign.getUserInfo(oldOpenEntity.getCreatorId());
        if (ResultCode.SUCCESS.getCode() != userInfo.getCode()) {
            throw new SysException(userInfo.getMsg());
        }
        UserEntity userDto = JSON.parseObject(JSON.toJSONString(userInfo.getResult().get("data")), UserEntity.class);
        if (null == userDto) {
            throw new SysException("当前用户不存在");
        }
        return userDto;
    }

    /**
     * * 开立签收
     * * 通过：
     * * 1、修改签收状态，已签收
     * * 2、修改签收审批流
     * * 3、添加我的票据
     * * 4、授信扣减
     *
     * @param noticeApprovalResult 审批结果
     * @param ticketProcessEntity  开立流程
     * @param user                 登录用户
     * @throws SysException 业务异常
     */
    private void passTicketOpenSign(TicketOpenEntity oldOpenEntity, UserEntity userDto
            , NoticeApprovalResult noticeApprovalResult, TicketProcessEntity ticketProcessEntity
            , User user) throws SysException {
        // 修改签收状态，已签收
        String ticketOpenId = ticketProcessEntity.getTicketOpenId();
        TicketOpenEntity ticketOpenEntity = new TicketOpenEntity();
        ticketOpenEntity.setId(ticketOpenId);
        ticketOpenEntity.setTicketStatus(TicketStatusEnum.NORMAL_HOLDING.getCode());
        ticketOpenEntity.setApproveStatus(ApproveStatusEnum.RECEIVE_ENTERPRISE_PASS.getCode());
        this.updateById(ticketOpenEntity);
        //修改签收审批流
        TicketProcessEntity processEntity = new TicketProcessEntity();
        processEntity.setApproveStatus(ApproveProcessEnum.APPROVE_PROCESS_SUCESS.getCode());
        processEntity.setRemark(noticeApprovalResult.getApprovalRemark());
        processEntity.setId(ticketProcessEntity.getId());
        processEntity.setUpdaterId(user.getId());
        ticketProcessService.updateById(processEntity);
        //添加我的票据
        String ticketId = ticketService.saveTicketByOpenId(ticketOpenId, user);
        //授信扣减
//        institutyCreditService.processCredit(this.getById(ticketId), CreditStatusProcessEnum.CREDIT_REDUCE.getCode(), null);
        //签收审核通过，给复核人申请人、抄送人发站内信
        this.sendOpenMsg(oldOpenEntity, noticeApprovalResult.getBeforeReceiveUserIds(), userDto, MailMessageConstant.TICKET_OPEN_SIGN_PASS_MSG);
        //签收审核通过，给平台审核人员人员发站内信
        //todo 获取平台审批权限用户接口
        List<String> platIds = new ArrayList<>();
        noticeApprovalResult.setReceiveUserIds(platIds);
        this.sendSignMsg(oldOpenEntity, noticeApprovalResult, user, MailMessageConstant.SUPPLIER_SIGN_PASS_MSG);

        //保存操作日志
        ticketOperateLogService.saveTicketLog(LogActionTypeEnum.TICKET_TYPE_OPEN.getCode(), oldOpenEntity.getId()
                , TicketLogStatusEnum.OPEN_PENDING_SIGN.getCode(), OperateLogTypeEnum.SIGN.getCode(), TicketLogStatusEnum.NORMAL_HOLDING.getCode(), user
                , noticeApprovalResult.getApprovalRemark(),
                oldOpenEntity.getReceiveEnterpriseId(), oldOpenEntity.getReceiveEnterpriseName(),
                oldOpenEntity.getOpenEnterpriseId(), oldOpenEntity.getOpenEnterpriseName(), "", "0", null, null);

        this.saveOpenSignFlowChart(oldOpenEntity, "1", ticketId);
    }

    private void saveOpenSignFlowChart(TicketOpenEntity oldOpenEntity, String flowColor, String ticketId) throws SysException {
        List<TicketFlowChartEntity> ticketFlowChartEntities = ticketFlowChartService.listByTicketOpenId(oldOpenEntity.getId());
        ticketFlowChartEntities.forEach(e -> {
            if (!"0".equals(e.getParentId())) {
                e.setTicketStatus(flowColor);
                e.setTicketId(ticketId);
                ticketFlowChartService.updateById(e);
            }
        });
    }


    /**
     * 签收审核结束，给开立审核人员发站内信
     *
     * @param oldOpenEntity        开立申请
     * @param beforeReceiveUserIds 开立审核人
     * @param userDto              登录用户
     * @throws SysException 业务异常
     */
    private void sendOpenMsg(TicketOpenEntity oldOpenEntity, List<String> beforeReceiveUserIds, UserEntity userDto, String msgContent) throws SysException {
        if (CollectionUtils.isEmpty(beforeReceiveUserIds)) {
            beforeReceiveUserIds = new ArrayList<>();
        }
        //添加审批人
        beforeReceiveUserIds.add(oldOpenEntity.getCreatorId().toString());
        String msg = msgContent.replace("{{签收方}}", oldOpenEntity.getReceiveEnterpriseName())
                .replace("{{金额}}", NumberUtil.numberFormatString(oldOpenEntity.getTicketAmount()))
                .replace("{{申请方}}", oldOpenEntity.getOpenEnterpriseName());
        //开立签收的供应商审批完成，给核心企业用户发消息
        User user = new User();
        user.setId(userDto.getId());
        user.setUsername(userDto.getUserName());
        user.setEnterpriseId(userDto.getEnterpriseId());
//        //消息通知
        sendMsgService.sendMsgToAllNotifier(oldOpenEntity.getId(), beforeReceiveUserIds, GotoTypeEnum.GOTO_TYPE_OPEN.getCode().toString(), msg, user, null);
    }
}